package com.yunji.common.utils.uuid;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.Assert;

import javax.annotation.PostConstruct;

/**
 * @Project: yunji-coffee
 * @Package:    com.yunji.common.utils.uuid
 * @Title:      SnowFlake
 * @Description:
 * 雪花算法生成唯一值
 *
 * History:
 * Date                     Version     Author          Summary
 * ============================================================
 * 2024-04-07 11:32:18      V1.0        HuaAo       新建类
 */
public class SnowFlake
{
    private static final Logger logs = LoggerFactory.getLogger(SnowFlake.class);

    // 锚定时间
    private static final long ANCHOR_TIME = 1288834974657L;

    private static final long WORKER_ID_BITS = 5L;  // 工作区域最大长度为5位
    private static final long CENTER_ID_BITS = 5L;  // 数据中心最大长度为5位
    private static final long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS); // 工作区域最大值
    private static final long MAX_CENTER_ID = ~(-1L << CENTER_ID_BITS); // 数据中心最大值
    private static final long SEQUENCE_BITS = 12L;  // 序列号最大长度为12位
    private static final long SEQUENCE_MASK = ~(-1L << SEQUENCE_BITS); // 序列号最大值

    private static final long WORKER_OFFSET = SEQUENCE_BITS; // 工作区域偏移量 12位
    private static final long CENTER_OFFSET = SEQUENCE_BITS + WORKER_ID_BITS; // 数据中心偏移量 12 + 5 = 17位
    private static final long TIMESTAMP_OFFSET = SEQUENCE_BITS + WORKER_ID_BITS + CENTER_ID_BITS; // 时间戳左偏移量 12 + 5 + 5 = 22位

    @Value("${snowflake.worker.id:1}")
    private long initWorkerId;
    @Value("${snowflake.center.id:1}")
    private long initCenterId;
    @Value("${snowflake.sequence:1}")
    private long initSequence;

    // 下面两个每个5位，加起来就是10位的工作机器id
    // 工作区域id
    private long workerId;
    // 数据中心id
    private long centerId;
    // 12位的序列号
    private long sequence;

    //上次时间戳，初始值为负数
    private long lastTimestamp = -1L;

    @PostConstruct
    public void init()
    {
        loadData(initWorkerId, initCenterId, initSequence);
    }

    /**
     * @Title	SnowFlakeUtils
     * @Desc	初始化雪花算法, 三个必传参数
     * @Date	2023-12-30 10:54:59
     * @Author	HuaAo
     * @Param	workerId 工作区域标识
     * @Param	centerId 数据中心线标识
     * @Param	sequence 初始序列号
     */
    public SnowFlake(long workerId, long centerId, long sequence)
    {
        loadData(workerId, centerId, sequence);
    }

    private void loadData(long workerId, long centerId, long sequence)
    {
        // sanity check for workerId
        Assert.isTrue((workerId >= 0 && workerId <= MAX_WORKER_ID), String.format("worker id can't be greater than %d or less than 0", MAX_WORKER_ID));
        Assert.isTrue((centerId >= 0 && centerId <= MAX_CENTER_ID), String.format("center id can't be greater than %d or less than 0", MAX_CENTER_ID));
        logs.info("worker starting. timestamp left shift 【{}】, datacenter id bits 【{}】, worker id bits 【{}】, sequence bits 【{}】, worker id 【{}】",
                TIMESTAMP_OFFSET, CENTER_ID_BITS, WORKER_ID_BITS, SEQUENCE_BITS, workerId);
        this.workerId = workerId;
        this.centerId = centerId;
        this.sequence = sequence;
    }

    /**
     * @Desc	下一个ID生成算法
     * @Date	2023-11-20 10:25:39
     * @Author	HuaAo
     * @return	long 生成唯一ID
     */
    public synchronized long nextId()
    {
        long timestamp = currentTimeMillis();
        // 获取当前时间戳如果小于上次时间戳，则表示时间戳获取出现异常
        Assert.isTrue((timestamp >= lastTimestamp), String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));

        // 获取当前时间戳如果等于上次时间戳(同一毫秒内),则在序列号加一;否则序列号赋值为0,从0开始。
        if (lastTimestamp == timestamp)
        {
            sequence = (sequence + 1) & SEQUENCE_MASK;
            if (sequence == 0) { timestamp = nextTimeMillis(lastTimestamp); }
        }
        else
        {
            sequence = 0;
        }

        // 将上次时间戳值刷新
        lastTimestamp = timestamp;

        /**
         * 返回结果：
         * (timestamp - ANCHOR_TIME) << TIMESTAMP_OFFSET) 表示将时间戳减去初始时间戳，再左移相应位数
         * (centerId << CENTER_OFFSET) 表示将数据id左移相应位数
         * (workerId << WORKER_OFFSET) 表示将工作id左移相应位数
         * | 是按位或运算符，例如：x | y，只有当x,y都为0的时候结果才为0,其它情况结果都为1。
         * 因为个部分只有相应位上的值有意义,其它位上都是0,所以将各部分的值进行 | 运算就能得到最终拼接好的id
         */
        return ((timestamp - ANCHOR_TIME) << TIMESTAMP_OFFSET) | (centerId << CENTER_OFFSET) | (workerId << WORKER_OFFSET) | sequence;
    }

    /**
     * @Desc	获取下次时间戳
     * @Date	2023-11-20 10:02:00
     * @param	lastTimestamp 最后一次时间戳
     * @return	获取唯一时间戳
     */
    private long nextTimeMillis(long lastTimestamp)
    {
        long timestamp = currentTimeMillis();
        while (timestamp <= lastTimestamp)
        {
            timestamp = currentTimeMillis();
        }
        return timestamp;
    }

    /**
     * @Desc	获取系统时间戳
     * @Date	2023-11-20 10:02:27
     * @return	当前系统时间戳
     */
    private long currentTimeMillis()
    {
        return System.currentTimeMillis();
    }

    public static void main(String[] args)
    {
        SnowFlake snowFlake = new SnowFlake(1,1,1);
        for (int i = 0; i < 1000; i++)
        {
            long uuid = snowFlake.nextId();
            logs.info("雪花算法生成唯一ID【{}】", uuid);
        }
    }
}